\section{【实现】初始化中断控制器}\label{ux5b9eux73b0ux521dux59cbux5316ux4e2dux65adux63a7ux5236ux5668}

80386把中断号0～31分配给陷阱、故障和非屏蔽中断，而把32～47之间的中断号分配给可屏蔽中断。可屏蔽中断的中断号是通过对中断控制器的编程来设置的。下面描述了对8259A中断控制器初始化过程。

8259A通过两个I/O地址来进行中断相关的数据传送，对于单个的8259A或者是两级级联中的主8259A而言，这两个I/O地址是0x20和0x21。对于两级级联的从8259A而言，这两个I/O地址是0xA0和0xA1。8259A有两种编程方式，一是初始化方式，二是工作方式。在操作系统启动时，需要对8959A做一些初始化工作，即实现8259A的初始化方式编程。8259A中的四个中断命令字（ICW）寄存器用来完成初始化编程，其含义如下：

\begin{itemize}
\item
  ICW1：初始化命令字。
\item
  ICW2：中断向量寄存器，初始化时写入高五位作为中断向量的高五位，然后在中断响应时由8259根据中断源（哪个管脚）自动填入形成完整的8位中断向量（或叫中断类型号）。
\item
  ICW3： 8259的级联命令字，用来区分主片和从片。
\item
  ICW4：指定中断嵌套方式、数据缓冲选择、中断结束方式和CPU类型。
\end{itemize}

8259A初始化的过程就是写入相关的命令字，8259A内部储存这些命令字，以控制8259A工作。有关的硬件可看附录补充资料。这里只把ucore对8259A的初始化过程（在picirq.c中的pic\_init函数实现）描述一下:

\begin{lstlisting}
//此时系统尚未初始化完毕，故屏蔽主从8259A的所有中断

outb(IO_PIC1 + 1, 0xFF);
outb(IO_PIC2 + 1, 0xFF);

// 设置主8259A的ICW1，给ICW1写入0x11，0x11表示（1）外部中断请求信号为上升沿触发有效，（2）系统中有多片8295A级联，（3）还表示要向ICW4送数据

// ICW1设置格式为:  0001g0hi  
//    g:  0 = edge triggering, 1 = level triggering  
//    h:  0 = cascaded PICs, 1 = master only  
//    i:  0 = no ICW4, 1 = ICW4 required  

outb(IO_PIC1, 0x11);

// 设置主8259A的ICW2:  给ICW2写入0x20，设置中断向量偏移值为0x20，即把主8259A的IRQ0-7映射到向量0x20-0x27

outb(IO_PIC1 + 1, IRQ_OFFSET);

// 设置主8259A的ICW3:  ICW3是8259A的级联命令字，给ICW3写入0x4，0x4表示此主中断控制器的第2个IR线（从0开始计数）连接从中断控制器。

outb(IO_PIC1 + 1, 1 << IRQ_SLAVE);

//设置主8259A的ICW4：给ICW4写入0x3，0x3表示采用自动EOI方式，即在中断响应时，在8259A送出中断矢量后，自动将ISR相应位复位；并且采用一般嵌套方式，即当某个中断正在服务时，本级中断及更低级的中断都被屏蔽，只有更高的中断才能响应。

// ICW4设置格式为:  000nbmap
//    n:  1 = special fully nested mode
//    b:  1 = buffered mode
//    m:  0 = slave PIC, 1 = master PIC
//      (ignored when b is 0, as the master/slave role
//      can be hardwired).
//    a:  1 = Automatic EOI mode
//    p:  0 = MCS-80/85 mode, 1 = intel x86 mode
outb(IO_PIC1 + 1, 0x3);

//设置从8259A的ICW1：含义同上

outb(IO_PIC2, 0x11);    // ICW1

//设置从8259A的ICW2：给ICW2写入0x28，设置从8259A的中断向量偏移值为0x28

outb(IO_PIC2 + 1, IRQ_OFFSET + 8);  // ICW2

//0x2表示此从中断控制器链接主中断控制器的第2个IR线

outb(IO_PIC2 + 1, IRQ_SLAVE);   // ICW3

//设置主8259A的ICW4：含义同上

outb(IO_PIC2 + 1, 0x3); // ICW4

//设置主从8259A的OCW3：即设置特定屏蔽位（值和英文解释不一致），允许中断嵌套；不查询；将读入其中断请求寄存器IRR的内容

// OCW3设置格式为:  0ef01prs
//   ef:  0x = NOP, 10 = clear specific mask, 11 = set specific mask
//    p:  0 = no polling, 1 = polling mode
//   rs:  0x = NOP, 10 = read IRR, 11 = read ISR
outb(IO_PIC1, 0x68);    // clear specific mask
outb(IO_PIC1, 0x0a);    // read IRR by default

outb(IO_PIC2, 0x68);    // OCW3
outb(IO_PIC2, 0x0a);    // OCW3

//初始化完毕，使能主从8259A的所有中断

if (irq_mask != 0xFFFF) {
    pic_setmask(irq_mask);
}
\end{lstlisting}

